/*
 *  Copyright (C) 2010 Matthias Buch-Kromann <mbk.isv@cbs.dk>
 * 
 *  This file is part of the MatrixParser package.
 *  
 *  The MatrixParser program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.osdtsystem.matrixparser.features;

import org.osdtsystem.matrixparser.data.CONLLSentence;
import org.osdtsystem.matrixparser.featureextraction.FirstOrderExtractorLabelled;
import org.osdtsystem.matrixparser.featureextraction.FirstOrderExtractorUnlabelled;

/**
 *
 * @author Matthias Buch-Kromann <mbk.isv@cbs.dk>
 */
public class DefaultMstCache implements MstCache {
    public CONLLSentence sentence;
    public FirstOrderExtractorUnlabelled unlabelledExtractor;
    FirstOrderExtractorLabelled labelledExtractor;
    FeatureHandler labelAlphabet;
    FeatureVector[] unlabelledFeatureVectors;
    FeatureVector[] labelledFeatureVectors;
    final int nodes;
    final int labels;


    public DefaultMstCache(CONLLSentence sentence,
            FirstOrderExtractorUnlabelled unlabelledMstExtractor,
            FirstOrderExtractorLabelled labelledMstExtractor,
            FeatureHandler labelAlphabet) {
        this.sentence = sentence;
        this.unlabelledExtractor = unlabelledMstExtractor;
        this.labelledExtractor = labelledMstExtractor;
        this.labelAlphabet = labelAlphabet;
        nodes = sentence.size();
        labels = labelAlphabet.alphabetSize();
        unlabelledFeatureVectors = new FeatureVector[nodes * nodes];
        labelledFeatureVectors = new FeatureVector[nodes * labels * 2 * 2];
    }

    final int _unlabelledArrayPos(int head, int dependent) {
        if (head < 0 || head >= nodes)
            throw new IllegalArgumentException("MstCache: max=" + nodes + " head=" + head);
        if (dependent < 0 || dependent >= nodes)
            throw new IllegalArgumentException("MstCache: max=" + nodes + " dependent=" + dependent);
        return head * nodes + dependent;
    }
    
    final int _labelledArrayPos(int node, int label, boolean headBeforeDependent, boolean isDependent) {
        if (node < 0 || node >= nodes)
            throw new IllegalArgumentException("MstCache: max=" + nodes + " node=" + node);
        if (label < 0 || label >= labels)
            throw new IllegalArgumentException("MstCache: max=" + labels + " label=" + label);
        int pos = (node * labels + label) * 4 + (headBeforeDependent ? 2 : 0) + (isDependent ? 1 : 0);
        return pos;
    }

    public final FeatureVector unlabelledFeatures(int head, int dependent) {
        // Use cached value if possible
        int arrayPos = _unlabelledArrayPos(head, dependent);
        FeatureVector cached = unlabelledFeatureVectors[arrayPos];
        if (cached != null) {
            //System.err.print("{U" + arrayPos + "} ");
            return cached;
        }

        // Compute new value
        unlabelledFeatureVectors[arrayPos] = cached = FeatureVectorFactory.makeNodeFeatureVector();
        unlabelledExtractor.extractUnlabelled(cached, sentence, head, dependent);
        return cached;
    }

    public FeatureVector labelledDependentFeatures(int head, int dependent, int label) {
        // Use cached value if possible
        int arrayPos = _labelledArrayPos(dependent, label, head < dependent, true);
        FeatureVector cached = labelledFeatureVectors[arrayPos];
        if (cached != null) {
            //System.err.print("{L" + arrayPos + "} ");
            return cached;
        }

        // Compute new value
        labelledFeatureVectors[arrayPos] = cached = new SparseBinaryFeatureVector();
        labelledExtractor.extractLabelled(cached,
                sentence, label, dependent, head < dependent, true);
        return cached;
    }

    public FeatureVector labelledHeadFeatures(int head, int dependent, int label) {
        // Use cached value if possible
        int arrayPos = _labelledArrayPos(head, label, head < dependent, false);
        FeatureVector cached = labelledFeatureVectors[arrayPos];
        if (cached != null)
            return cached;

        // Compute new value
        labelledFeatureVectors[arrayPos] = cached = new SparseBinaryFeatureVector();
        labelledExtractor.extractLabelled(cached,
                sentence, label, head, head < dependent, false);
        return cached;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString());
        sb.append("DefaultMstCache: ").append("" + unlabelledNonNull())
                .append(":")
                .append("" + labelledNonNull());
        return sb.toString();
    }

    int labelledNonNull() {
        int count = 0;
        for (int i = 0; i < labelledFeatureVectors.length; ++i)
            if (labelledFeatureVectors[i] != null)
                count++;
        return count;
    }

    int unlabelledNonNull() {
        int count = 0;
        for (int i = 0; i < unlabelledFeatureVectors.length; ++i)
            if (unlabelledFeatureVectors[i] != null)
                count++;
        return count;
    }
}
